package cz.vargagab.repo;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.jboss.logging.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.remoting.rmi.RmiProxyFactoryBean;
import org.springframework.stereotype.Service;

import cz.vargagab.domain.ClientRmiNode;
import cz.vargagab.domain.LogObject;
import cz.vargagab.domain.RmiNode;
import cz.vargagab.domain.ServerRmiNode;

public class ExternalServerServiceImpl implements ExternalServerService {

	@Autowired
	ServerRmiNode thisNode;
	
	@Autowired
	InternalServerService internalServerService;
	
	Logger log = Logger.getLogger(ExternalServerServiceImpl.class);
	
	public void login (ClientRmiNode node){
		internalServerService.getLog().add(new LogObject(node.getName() + " tries to log in"));
		
		//vytvorenie RMI interfacu, s ktorym moze server komunikovat s klientom
		RmiProxyFactoryBean temp = new RmiProxyFactoryBean();
		temp.setServiceInterface(ExternalClientService.class);
		temp.setServiceUrl("rmi://"+node.getIpAddress()+":1099/"+node.getClientToServiceIface());
		temp.afterPropertiesSet();
		ExternalClientService clientService = (ExternalClientService) temp.getObject();

		internalServerService.getMapOfNodes().put(node.getName(),node);
		clientService.updateServer(thisNode); // aby klient vedel udaje o servri
		
		internalServerService.getClientInterfaces().put(node,clientService);
		
		List<ClientRmiNode> clientNodes = new ArrayList<ClientRmiNode>();
		clientNodes.addAll((internalServerService.getClientInterfaces().keySet())); // clientNodes obsahuje vsetky aktualne zalogovane klientske nody
			for (ClientRmiNode clientNode : internalServerService.getClientInterfaces().keySet()){
				try{
					internalServerService.getClientInterfaces().get(clientNode).updateClients(clientNodes); // na interface klienta posle zoznam vsetkych nodov
				}catch(Exception e){
					internalServerService.getLog().add(new LogObject("Not updating "+node.getName()+", cause it is down"));
				}
			}
		
		internalServerService.getLog().add(new LogObject(node.getName() + " logs in successfuly"));
	}
	
	public void logout (ClientRmiNode node){
		internalServerService.getLog().add(new LogObject(node.getName() + " tries to log out"));
		internalServerService.getMapOfNodes().remove(node.getName());
		
		ExternalClientService service =((ExternalClientService) internalServerService.getClientInterfaces().get(node));
		internalServerService.getClientInterfaces().remove(node);
		//teraz uz je odhlasovany node odmazany zo vsetkych struktur servra, ale este vyuzijem jeho RMI interface, aby som mu povedal, ze odhlasenie prebehlo spravne
		
		try{
			service.dc();
			service.updateClients(new ArrayList<ClientRmiNode>());
		}catch(Exception e){
			internalServerService.getLog().add(new LogObject("Not updating "+node.getName()+", cause it is down (logout sent by sibling node)"));
		}
		
		List<ClientRmiNode> clientNodes = new ArrayList<ClientRmiNode>();
		clientNodes.addAll((internalServerService.getClientInterfaces().keySet())); 
		//client nodes obsahuje klientov bez uzlu, ktory sa chce odhlasit - updateujem vsetkych klientov aby vedeli, ze niekto sa uz odhlasil
		for (ClientRmiNode otherNodes : internalServerService.getClientInterfaces().keySet()){
			try{
				internalServerService.getClientInterfaces().get(otherNodes).updateClients(clientNodes);
			}catch(Exception e){
					internalServerService.getLog().add(new LogObject("Not updating "+node.getName()+", cause it is down"));
			}
		}

		internalServerService.getLog().add(new LogObject(node.getName() + " logs out successfuly"));
	}

	@Override
	public int readData(String nameOfReadingNode) {
		synchronized (this) { //synchronizacia pristupu - poziadavky sa radia do poradia ako prisli
			int retValue = internalServerService.readData(nameOfReadingNode);
			 if (!thisNode.getName().equals(nameOfReadingNode)) { //if the server reads  the data, dont log it after every refresh
				 internalServerService.getLog().add(new LogObject(nameOfReadingNode + " reads data, gets " +retValue));
			 }
			return retValue;
		}
	}

	@Override
	public void writeData(String nameOfWritingNode, int data) {
		synchronized (this) { //synchronizacia pristupu - poziadavky sa radia do poradia ako prisli
			internalServerService.writeData(nameOfWritingNode, data);
			internalServerService.getLog().add(new LogObject(nameOfWritingNode + " writes data, writes " +data));
		}
	}

	@Override
	public Map<String, RmiNode> mapOfNodes() {
		return internalServerService.getMapOfNodes();
	}
	
	@Override
	public void testConnection() {
		System.out.println("Testing connection before calling ExternalServerService" );
	}
	
}
